﻿#[derive(Debug)]
pub enum InstructionType {
    Fetch,          // 从全局变量中获取一个值并压入栈顶
    Store,          // 将栈顶的值存入全局变量
    Push,           // 将一个常量压入栈顶
    Pop,            // 弹出栈顶的值
    Add,            // 弹出两个值并将它们相加，然后将结果压入栈顶
    Sub,            // 弹出两个值并将它们相减，然后将结果压入栈顶
    Lt,             // 弹出两个值并将它们比较，然后将结果压入栈顶
    Jz,             // 弹出栈顶的值，如果它为0，则跳转到指定的地址
    Jnz,            // 弹出栈顶的值，如果它不为0，则跳转到指定的地址
    Jmp,            // 无条件跳转到指定的地址
    Halt,           // 停止执行
    Integer(isize), // 整数常量
    Address(usize), // 地址常量
}
#[derive(Default)]
pub struct VM {
    pub globals: [isize; 26],   // 堆区内存, 存储变量的值
    code: Vec<InstructionType>, // 指令序列
    pc: usize,                  // 程序计数器
    stack: Vec<isize>,          // 栈
    tracing: bool,              // 是否跟踪执行
}

impl VM {
    #[must_use]
    pub fn new() -> Self {
        VM { ..VM::default() }
    }

    pub fn trace_on(&mut self) {
        self.tracing = true;
    }

    fn get_const(&mut self) -> isize {
        let InstructionType::Integer(n) = self.code[self.pc] else {
            panic!(
                "Bad code, expected integer constant, got {:?}",
                self.code[self.pc]
            );
        };
        self.pc += 1;
        n
    }

    fn get_address(&mut self) -> usize {
         if let InstructionType::Address(n) = self.code[self.pc] {
            self.pc += 1;
            n
        } else {
            panic!(
                "Bad code, expected address constant, got {:?}",
                self.code[self.pc]
            );
        }
    }
    fn top(&mut self) -> isize {
        self.stack[self.stack.len() - 1]
    }

    pub fn run(&mut self, code: Vec<InstructionType>) {
        self.code = code;
        self.pc = 0;
        loop {
            let insn = &self.code[self.pc];

            if self.tracing {
                println!("{:4}: {:?}  (stack: {:?})", self.pc, insn, self.stack);
            }

            self.pc += 1;
            match insn {
                InstructionType::Integer(_) | InstructionType::Address(_) => {
                    panic!("Can't execute middle of instructions")
                }
                InstructionType::Halt => {
                    break;
                }
                InstructionType::Fetch => {
                    let a = self.get_address();
                    self.stack.push(self.globals[a]);
                }
                InstructionType::Store => self.globals[self.get_address()] = self.top(),
                InstructionType::Push => {
                    let v = self.get_const();
                    self.stack.push(v);
                }
                InstructionType::Pop => {
                    self.stack.pop().unwrap();
                }
                InstructionType::Add => {
                    let b = self.stack.pop().unwrap();
                    let a = self.stack.pop().unwrap();
                    self.stack.push(a + b); 
                }
                InstructionType::Sub => {
                    let b = self.stack.pop().unwrap();
                    let a = self.stack.pop().unwrap();
                    self.stack.push(a - b);
                }
                InstructionType::Lt => {
                    let b = self.stack.pop().unwrap(); 
                    let a = self.stack.pop().unwrap();
                    self.stack.push(isize::from(a < b));
                }
                InstructionType::Jmp => self.pc = self.get_address(),
                InstructionType::Jz => {
                    let n = self.get_address();
                    let v = self.stack.pop().unwrap();
                    if v == 0 {
                        self.pc = n;
                    }
                }
                InstructionType::Jnz => {
                    let n = self.get_address();
                    let v = self.stack.pop().unwrap();
                    if v != 0 {
                        self.pc = n;
                    }
                }
            }
        }
    }
}
